/* Peter Banda, Daniel Cristofani, Dominic Tippabattuni                     */
/* 975530978    971012079          958842138                                */
/* VMM_Sim Implementation (assignment 5)                                    */
/* CS 538, Portland State University, Fall 2010                             */
/* Turned in November 18, 2010                                              */

/* inputhandler.c contains the code to read ASCII input and divide it       */
/* into commands, which it then acts on by calling other modules. It uses   */
/* essentially a two-state machine--one for "reading to find command" and   */
/* and the other for "reading address".                                     */

// headers
#include "vmm.h"
#include <ctype.h>

// Count of lines and characters, for error reporting.
unsigned long line = 1, character = 0;

bool iscommand(int c)
{
    return c == 'w' || c == 'W'
	|| c == 'r' || c == 'R'
	|| c == 'l' || c == 'L'
	|| c == 'v' || c == 'V'
	|| c == 't' || c == 'T'
        || c == 'p' || c == 'P';
}

int tohexdigit(int c)
{
    if (isdigit(c))
	return c - '0';
    if (c >= 'a' && c <= 'f')
	return 10 + c - 'a';
    if (c >= 'A' && c <= 'F')
	return 10 + c - 'A';
    return -1;
}

// A package around getchar to keep the counts updated.
// Separating this is why the counts couldn't be local to one function.
int getchar_and_count()
{
    int c;
    c = getchar();
    character++;
    if (c == '\n') {
	character = 0;
	line++;
    }
    return c;
}

//This is the main input-handling function
void inputHandler(void)
{
    int c, command;
    unsigned long address;
    while ((c = getchar_and_count()) != EOF) {	// State 1
	if (c == 'l' || c == 'L') {
	    lFlag = true;
	    addToCommandLog(l, 0);
	} else if (c == 't' || c == 'T') {
	    tFlag = true;
	    addToCommandLog(t, 0);
	} else if (c == 'v' || c == 'V') {
	    printVersionInfo();         // Needn't record, no history will be printed.
	} else if (iscommand(c)) {	// r or R or w or W or p or P: Enter State 2
	    command = tolower(c);
	    address = 0;
	    while ((c = getchar_and_count()) != EOF && !iscommand(c)) {
		if (isxdigit(c)) {
		    // 1<<28 = 2^28. If the address is already >= 2^28 it will be > 32 bits after the following multiplication.
		    // That's why we give a warning here.
		    if (address >= (1 << 28)) {
			warning
			    ("Address too long (detected at line %lu, character %lu). Addresses must be from 0 to (2^32-1).",
			     line, character);
		        address &= ((1 << 28) - 1);
		    }
		    address *= 16;
		    address += tohexdigit(c);
		} else if (!(isspace(c) || c == 'x' || c == 'X')) {
		    warning
			("Unexpected character %c in input at line %lu, character %lu.",
			 c, line, character);
		}
	    }
	    if (iscommand(c)) {
		ungetc(c, stdin);	//Push command back to be read
		character--;	//by state 1.
	    }
	    if (noOfPageFrames == 0) { //If this was not set on command line, it will be set on the first r, w, or p command.
		if (command == 'p') {  //If it's p, we use the number;
		    addToCommandLog(p, address);
		    if (address < 3) {
			warning
			    ("3 is the minimum number of page frames required. Allocating 3 page frames.");
			noOfPageFrames = 3;
		    } else {
			noOfPageFrames = address;
		    }
		} else {                //if it was r or w, we use the default 10 frames.
		    noOfPageFrames = DEFAULT_PAGE_FRAME_COUNT;
		}
	    } else if (command == 'p') { //If the page frame count is already set, it's too late for a p command.
		warning ("The page frame count has already been set to %d, and cannot be changed at this point.",noOfPageFrames);
		addToCommandLog(p, address);
	    }
            if (command == 'w') {
		write(address);
		addToCommandLog(w, address);
	    } else if (command == 'r') {
		read(address);
		addToCommandLog(r, address);
	    }
	    if (c == EOF) {
		break;
	    }
	} else if (!isspace(c)) {
	    warning
		("Unexpected character %c in input at line %lu, character %lu.",
		 c, line, character);
	}
    }
}
